﻿using System;
using System.Collections;
using System.Collections.Generic;
using Drill.Core.Utilities;

namespace Drill.Configuration.ConfigFile
{
	public class BaseConfigurationElementCollectionEnumerator<TConfigInterface> : IEnumerator<TConfigInterface>
		where TConfigInterface : class
	{
		private readonly IEnumerator _enumerator;

		public BaseConfigurationElementCollectionEnumerator(IEnumerator enumerator)
		{
			_enumerator = enumerator;
		}

		#region " IEnumerator<TConfigInterface> Interface Implementation "

		/// <summary>
		/// Advances the enumerator to the next element of the collection.
		/// </summary>
		/// <returns>
		/// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
		/// </returns>
		/// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created. </exception><filterpriority>2</filterpriority>
		public bool MoveNext()
		{
			CheckDisposed();
			return _enumerator.MoveNext();
		}

		/// <summary>
		/// Sets the enumerator to its initial position, which is before the first element in the collection.
		/// </summary>
		/// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created. </exception><filterpriority>2</filterpriority>
		public void Reset()
		{
			CheckDisposed();
			_enumerator.Reset();
		}

		/// <summary>
		/// Gets the element in the collection at the current position of the enumerator.
		/// </summary>
		/// <returns>
		/// The element in the collection at the current position of the enumerator.
		/// </returns>
		public TConfigInterface Current
		{
			get
			{
				CheckDisposed();
				TConfigInterface item = _enumerator.Current as TConfigInterface;
				if (null == item)
				{
					throw new InvalidOperationException(
						string.Format("The enumerated items must be a type of {0}.", typeof(TConfigInterface).FullName)
						);
				}
				return item;
			}
		}

		/// <summary>
		/// Gets the current element in the collection.
		/// </summary>
		/// <returns>
		/// The current element in the collection.
		/// </returns>
		/// <exception cref="T:System.InvalidOperationException">The enumerator is positioned before the first element of the collection or after the last element.</exception><filterpriority>2</filterpriority>
		object IEnumerator.Current
		{
			get
			{
				CheckDisposed();
				return Current;
			}
		}

		#endregion " IEnumerator<TConfigInterface> Interface Implementation "

		#region " IDisposable Interface and Disposable Pattern Implementation "

		/// <summary>
		/// Gets a value indicating whether this instance is disposed.
		/// </summary>
		/// <value><c>true</c> if this instance is disposed; otherwise, <c>false</c>.</value>
		public bool IsDisposed { get; private set; }

		/// <summary>
		/// Performs managed and unmanaged resource cleanup.
		/// </summary>
		public void Dispose()
		{
			// NOTE: The Dispose() method is public.
			// NOTE: The Dispose() method is not virtual because it should not be overridden.

			if (false == IsDisposed)
			{
				Dispose(true);
				GC.SuppressFinalize(this);
			}
		}

		/// <summary>
		/// Performs managed and unmanaged resource cleanup.
		/// </summary>
		/// <param name="disposing"><c>true</c> if disposing, <c>false</c> if finalizing.</param>
		private void Dispose(bool disposing)
		{
			// NOTE: The Dispose(bool disposing) method is private.
			// NOTE: The Dispose(bool disposing) method is not virtual (because it is private).

			// When the operation is Dispose, call DisposeManagedResources AND DisposeUnmanagedResources.
			// When the operation is Finalize, only call DisposeUnmanagedResources.

			if (false == IsDisposed)
			{
				if (disposing)
				{
					DisposeManagedResources();
				}

				DisposeUnmanagedResources();

				// Set the _isDisposed flag.
				IsDisposed = true;
			}
		}

		protected virtual void DisposeManagedResources()
		{
			// NOTE: The DisposeManagedResources() method is protected.
			// NOTE: The DisposeManagedResources() method is virtual.

			// Clean up managed resources here.
			// * Call Dispose on all disposable object references
			// * Unsubscribe from all subscribed events
			// * Set managed object references to null

			DisposeUtility.DisposeIfPossible(_enumerator);
		}

		protected virtual void DisposeUnmanagedResources()
		{
			// NOTE: The DisposeUnmanagedResources() method is protected.
			// NOTE: The DisposeUnmanagedResources() method is virtual.

			// Clean up unmanaged resources here.
			// * Release handles
			// * Free allocated memory
			// * Dereference COM objects
			// * Set large fields to null
		}

		private void CheckDisposed()
		{
			if (IsDisposed)
			{
				string objectName = string.Format("{0}", GetType().Name);

				throw new ObjectDisposedException(objectName, string.Format("{0} has been disposed.", objectName));
			}
		}

		#endregion " IDisposable Interface and Disposable Pattern Implementation "
	}
}